/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Chat Library
// Project: molten-chat
// Developed by: Matthew Oltendorf
// Version: b2.1
// Updated on: May 24th, 2009
// Copyright (c) 2008-2009 Matthew Oltendorf
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */

// JavaScript Document

/*
	Unfinished Items:
		1.	Everything! LOL!
		2.
		3.
		4.
		5.
*/

/*
	Known Issues:
		1.	Does not work on load balanced server clusters; must be run on a single server. This is due to the shared memory cache.
		2.
		3.
		4.
		5.
*/

( function ( $ )
{
	/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
	// Name: molten_chat.
	// Type: Factory.
	// Title: MoltX Chat Ajax Object.
	// Description: Communicate efficiently.
	// Variables: history, user, room, form, message, users
	//		1.	history: The identifier of the history element within the page.
	//		2.	room: The name of the room this instance is for.
	//		3.	user: The user that is viewing the page.
	//			Default: false
	//		4.	form: The identifier of the form element within the page.
	//			Default: false
	//		5.	message: The identifier of the message element within the page.
	//			Default: false
	//		6.	users: The identifier of the users element within the page.
	//			Default: false
	// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
	var chat = function ( history, room, user, form, message, users )
	{
		// Bind jQuery to $.
		var $ = this.$ = window.jQuery;

		// Create a local reference to the instancing object.
		this.instancing = window.molten_chat_instancing;

		if ( typeof history === 'string' )
		{
			// Get the element from document.
			history = $ ( '#' + history );
		}
		else if ( typeof history === 'object' )
		{
			// Check if it's an array.
			if ( history.constructor === Array )
			{
				// Extract the first element.
				history = history [0];
			}

			// Check if the element is the proper type.
			if ( typeof history.nodeType === 'integer' )
			{
				if ( history.nodeType === ELEMENT_NODE )
				{
					// Make a new array.
					history = new Array ( history );
				}
			}
			else
			{
				// Fail.
				history = new Array ( );
			}
		}
		else
		{
			// Fail.
			history = new Array ( );
		}

		// Check if there was a match.
		if ( history.length > 0 )
		{
			// Register this object with the instancing object.
			this.instance = this.instancing.register ( this, room );

			if ( this.instance === false )
			{
				// We didn't get an instance, so we halt.
				return false;
			}

			// From this point on, this object has properly instantiated.

			// Set registration status to true.
			this.registered = true;

			// Reference history element.
			this.history = history [0];

			if ( typeof user === 'string')
			{
				this.localUser = user;
			}

			if ( typeof form === 'string' )
			{
				// Get the element from document.
				form = $ ( '#' + form );
			}
			else if ( typeof form === 'object' )
			{
				// Check if it's an array.
				if ( form.constructor === Array )
				{
					// Extract the first element.
					form = form [0];
				}

				// Check if the element is the proper type.
				if ( typeof form.nodeType === 'integer' )
				{
					if ( form.nodeType === ELEMENT_NODE )
					{
						// Make a new array.
						form = new Array ( form );
					}
				}
				else
				{
					// Fail.
					form = new Array ( );
				}
			}
			else
			{
				// Fail.
				form = new Array ( );
			}

			if ( typeof message === 'string' )
			{
				// Get the element from document.
				message = $ ( '#' + message );
			}
			else if ( typeof message === 'object' )
			{
				// Check if it's an array.
				if ( message.constructor === Array )
				{
					// Extract the first element.
					message = message [0];
				}

				// Check if the element is the proper type.
				if ( typeof message.nodeType === 'integer' )
				{
					if ( message.nodeType === ELEMENT_NODE )
					{
						// Make a new array.
						message = new Array ( message );
					}
				}
				else
				{
					// Fail.
					message = new Array ( );
				}
			}
			else
			{
				// Fail.
				message = new Array ( );
			}

			// Check if form and message is an element of the proper type.
			if ( form.length > 0 && message.length > 0 )
			{
				// Reference form element.
				this.form = form [0];

				// Reference message element.
				this.message = message [0];
			}

			if ( typeof users === 'string' )
			{
				// Get the element from document.
				users = $ ( '#' + users );
			}
			else if ( typeof users === 'object' )
			{
				// Check if it's an array.
				if ( users.constructor === Array )
				{
					// Extract the first element.
					users = users [0];
				}

				// Check if the element is the proper type.
				if ( typeof users.nodeType === 'integer' )
				{
					if ( users.nodeType === ELEMENT_NODE )
					{
						// Make a new array.
						users = new Array ( message );
					}
				}
				else
				{
					// Fail.
					users = new Array ( );
				}
			}
			else
			{
				// Fail.
				users = new Array ( );
			}

			if ( users.length > 0 )
			{
				// Reference users element.
				this.users = users [0];
			}

			// Activate this instance.
			this.instancing.activate ( this.instance );

			// Success.
			return true;
		}

		// Fail.
		return false;
	};

	// Prototype it!
	chat.prototype =
	{
		// jQuery!
		$: false,
		
		// Instancing related data.
		instance: false,
		instancing: false,
		registered: false,

		// User data.
		localUser: false,

		// Elements.
		elementForm: false,
		elementHistory: false,
		elementMessage: false,
		elementUsers: false,

		// Element tracking.
		history: new Array ( ),
		messages: new Array ( ),
		users: new Array ( ),

		// User tracking.
		usersKey: new Array ( ), // An indexed array containing the position of the user in the usersList.
		usersList: new Array ( ), // An indexed array containing the user data of each user.

		// Counters.
		action: 0,
		level: 0,
		line: 0,

		// Switches.
		connected: false,
		jumped: false,

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: response
		// Title: Handle response.
		// Variables: data, document
		//	1.	data: The data elements included in the root element of the document.
		//	2.	document: The room element within the document.
		// Description: Registers a chat client instance.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		response: function ( data, document )
		{
			// Bind jQuery to $.
			var $ = this.$;

			// Do stuff.
		}
	};

	/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
	// Name: molten_chat_instancing.
	// Type: Singleton.
	// Title: MoltX Chat Ajax Object Instancing.
	// Description: Used by molten_chat object to communicate with the server with several instances of itself.
	// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
	var instancing = function ( )
	{
		// Bind jQuery to $.
		var $ = this.$ = window.jQuery;
	};

	// Prototype it!
	instancing.prototype =
	{
		// jQuery!
		$: false,

		// It's alive...?
		active: false,
		initiated: false,
		
		// molten_chat instances.
		instances: new Array ( ),
		instancesActive: new Array ( ),
		namedInstances: new Array ( ),
		namedInstancesActive: new Array ( ),
		namedInstancesIndex: new Array ( ),

		// User to named instance link.
		userInstancing: new Array ( ),

		// Timing.
		lastRequest: 0,
		maxRequestSpeed: 1000,
		serverTimeOffset: 0,

		// Whether or not this window is the primary communication window.
		master: true,

		// URI.
		retrieveData: new Array ( ),
		sendData: new Array ( ),

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: initiate
		// Title: Initiate instancing object.
		// Variables: retrieve, send
		//	1.	retrieve: The URI to send a POST request to to retrieve data.
		//	2.	send: The URI to send a POST request containing a message to.
		// Description: Does nothing.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		initiate: function ( retrieve, send )
		{
			// Bind jQuery to $.
			var $ = this.$, room;

			// Verify retrieve URI input.
			if ( typeof retrieve === 'string' || typeof retrieve.uri === 'string' )
			{
				// Convert retrieve to an object.
				this.retrieveData = ( typeof retrieve.uri === 'string' ) ?
					retrieve :
					{
						data: {},
						uri: retrieve
					};

				// Verify send URI input.
				if ( typeof send === 'string' || typeof send.uri === 'string' )
				{
					// Convert send to an object.
					this.sendData = ( typeof send.uri === 'string' ) ?
						send :
						{
							data: {},
							uri: send
						};
				}

				// Properly initiated.
				this.initiated = true;

				// Start looping retrieval.
				this.start ( );

				return true;
			}

			return false;
		},

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: start
		// Title: Initiate looping retrieval.
		// Variables: None.
		// Description: Properly start retrieving data from server.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		start: function ( )
		{
			// Bind jQuery to $.
			var $ = this.$;

			if ( this.active === false && this.initiated === true )
			{
				for ( room in this.namedInstancesActive )
				{
					if ( room === true )
					{
						// Set chat state to active.
						this.active = true;

						// Start retrieving.
						this.retrieve ( );

						// Break/return.
						return true;
					}
				}
			}

			// Fail.
			return false;
		},

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: activate
		// Title: Activate chat object.
		// Variables: instance
		//	1.	instance: The instance of the chat object to activate.
		// Description: Activates a chat object instance, allowing the server to communicate with it.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		activate: function ( instance )
		{
			// Bind jQuery to $.
			var $ = this.$, i, index;

			// Check for valid input.
			if ( typeof instance === 'integer' )
			{
				// It must be greater than 0.
				if ( instance >= 0 )
				{
					if ( this.instancesActive [instance] === false )
					{
						// Set instance as active.
						this.instancesActive [instance] = true;

						// Check to see if the room is active yet.
						for ( i = 0; i < this.namedInstances.length; ++i )
						{
							index = $.inArray ( instance, this.namedInstances [i] );

							// -1 means it doesn't exist.
							if ( index !== -1 )
							{
								if ( this.namedInstancesActive [i] === false )
								{
									// Set room as active.
									this.namedInstancesActive [i] = true;

									// Start looping retrieval.
									this.start ( );
								}

								break;
							}
						}

						return true;
					}
				}
			}

			return false;
		},

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: register
		// Title: Register chat object.
		// Variables: object, name
		//	1.	object: The object of the chat client to register.
		//	2.	room: The name of the room the chat client is for.
		// Description: Registers a chat client instance.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		register: function ( object, room )
		{
			// Bind jQuery to $.
			var $ = this.$, identifier, index;

			// Get the current identifier of the chat object.
			identifier = this.instances.length;

			// Check for valid input.
			if ( typeof object === 'object' && typeof room === 'string' && room !== '' )
			{
				// "Register" this object.
				this.instances.push ( object );
				this.instancesActive.push ( false );

				index = $.inArray ( room, this.namedInstancesIndex );

				// -1 means it doesn't exist.
				if ( index === -1 )
				{
					// Create a new index.
					index = this.namedInstancesIndex.length;

					// Create a new named instance array.
					this.namedInstances = new Array ( );
					this.namedInstancesActive.push ( false );
					this.namedInstancesIndex.push ( room );
				}

				// Add this object to a named instances list.
				this.namedInstances [index].push ( identifier );
			}
			else
			{
				// Fail.
				return false;
			}

			// Return the identifier of the chat client instance so it can properly call itself.
			return identifier;
		},

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: retrieve
		// Title: Retrieve data from server.
		// Variables: None.
		// Description: Retrieve data from the server repeatedly.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		retrieve: function ( )
		{
			// Bind jQuery to $.
			var $ = this.$, milliseconds, nextRequest;
			
			milliseconds = ( new Date ( ) ).getTime ( );
			nextRequest = this.lastRequest + this.maxRequestSpeed;

			// Clear the timeout.
			clearTimeout ( this.retrieveTimeout );

			// Check if this function was randomly called for no reason.
			if ( nextRequest < milliseconds )
			{
				// Set the last request time. We subtract one to make sure we don't "overlap."
				this.lastRequest = --milliseconds;

				// Do stuff. jQuery! jQuery!
				// $.post ( ... );

				// Set a timeout for the next minimum query time.
				this.retrieveTimeout = setTimeout ( 'window.molten_chat_instancing.retrieve ( ); ', this.maxRequestSpeed );

				return true;
			}

			// Set a timeout for the next minimum query time.
			this.retrieveTimeout = setTimeout ( 'window.molten_chat_instancing.retrieve ( ); ', ( nextRequest - milliseconds ) );

			// Fail.
			return false;
		}
	};

	// Add objects to window element.
	window.molten_chat = chat, window.molten_chat_instancing = new instancing ( );
}
) ( window.jQuery );